/*
Kiwi Editor is a framework for building WYSIWYG web editors.
Copyright (C) 2011  Roosevelt de Lima Sardinha

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

Contact the author at roosevelt1@gmail.com (Roosevelt) 
*/

/**
 * Default behaviour of an EO. Every type of EO inherits from this class.
 * @author Roosevelt de Lima Sardinha
 * @param {String} id The unique identifier of an EO.
 * @param {Object} The object containing the properties to be initially set to the EO.
 * @constructor
 */
function EO(id, properties){	
	/**
	 * The object containing the properties to be initially set to the EO. 
	 * @type {Object}
	 */
	this.properties = properties;
	
	/**
	 * The property box where the EO show its properties to be changed by the user.
	 * @type {Object}
	 */
	this.propertyBox = this.properties.propertyBox;
	
	/**
	 * The instruction box where help to the user of this EO appears.
	 * @type {Object}
	 */
	this.instructionBox = this.properties.instructionBox;
	
	/**
	 * The EOM that manages this EO.
	 * @type {Object}
	 */
	this.EOM = this.properties.EOM;
	
	/**
	 * The unique id of this EO.
	 * @type {String}
	 */
	this.id = id;
	
	/**
	 * Keeps the status of the selection of the EO. True if it's selected, false if not.
	 * @type {Boolean}
	 */
	this.selected = false;
	
	// Events
	/**
	 * <b>Event</b><br/>
	 * Called when a property is set.
	 * @param {Object} EO The EO that the property is being set.
	 */
	this.onSetProperty = function(EO){};
	
	/**
	 * <b>Event</b><br/>
	 * Called when the EO is selected
	 * @param {Object} EO This EO when selected.
	 */
	this.onSelect = function(EO){};
	
	/**
	 * <b>Event</b><br/>
	 * Called when the EO is unselected.
	 * @param {Object} EO This EO when unselected.
	 */
	this.onUnselect = function(EO){};
	
	/**
	 * <b>Event</b><br/>
	 * Called when you drag an EO.
	 * @param {Int} x The left coordinate of the EO being dragged. Relative to the edition area.
	 * @param {Int} y The top coordinate of the EO being dragged. Relative to the edition area.
	 * @param {Object} EO This EO while being dragged.
	 */
	this.onDrag = function(x, y, EO){};
	
	/**
	 * <b>Event</b><br/>
	 * Called when you drop an EO.
	 * @param {Int} x The left coordinate of the EO when you drop it. Relative to the edition area.
	 * @param {Int} y The top coordinate of the EO when you drop it. Relative to the edition area.
	 * @param {Object} EO The EO when dropped. 
	 */
	this.onDrop = function(x, y, EO){};
	
	/**
	 * <b>Event</b><br/>
	 * Called when you remove this EO.
	 */
	this.onRemove = function(){};
	
	/**
	 * <b>Event</b><br/>
	 * Called when the cursor is over the EO in the edition area.
	 * @param {Event} event The mouse event of the DOM node.
	 * @param {Object} EO This EO when the mouse is over it. 
	 */
	this.onMouseOver = function(event, EO){};
	
	/**
	 * <b>Event</b><br/>
	 * Called when the cursor leaves the EO in the edition area. 
	 * @param {Event} event The mouse event of the DOM Node. 
	 * @param {Object} EO This EO when the mouse leaves it.
	 */
	this.onMouseOut = function(event, EO){};
	
	/**
	 * <b>Event</b><br/>
	 * Called when you press the button of the mouse over the EO.
	 * @param {Event} event The mouse event of the DOM Node.
	 * @param {Object} EO This EO when you press the button of the mouse over the EO. 
	 */
	this.onMouseDown = function(event, EO){};
	
	/**
	 * <b>Event</b><br/>
	 * Called when you double click the EO.
	 * @param {Event} event The mouse event of the DOM Node.
	 * @param {Object} EO This you when you double click it.
	 */
	this.onDblClick = function(event, EO){};
	
	/**
	 * Called when the EO show its properties in the property box.
	 * @param {Object} EO The EO when showing the properties 
	 */
	this.onShowProperties = function(EO){};
	
	/**
	 * Called when the EO show some help in the instruction box.
	 * @param {Object} EO The EO when it shows some help in the instruction box.
	 */
	this.onShowInstructions = function(EO){};
	
	
	// The same method in the EO implementation
	// Note: when a method of the same name exists here, the method of the user is renamed to user_METHODNAME(); First the framework calls the method in this class,
	// so be sure to call the user method.
	
	/**
	 * Changes a property of the EO to some value
	 * @param {String} propertyName The name of the property to be changed.
	 * @param {Any} value The value to be set to the property.
	 */	
	this.setProperty = function(property, value){
		switch(property){
            case 'left':
                this.setLeft(parseInt(value));
                break;
            case 'top':
                this.setTop(parseInt(value));
                break;
			case 'propertyBox':
				this.propertyBox = this.EOM.editionArea.workspace.getPropertyBoxById(value);
				break;
			case 'instructionBox':
				this.instructionBox = this.EOM.editionArea.workspace.getInstructionBoxById(value);
				break;
			default:
				break;
		}
			
		this.user_setProperty(property,value);
		this.onSetProperty(this);		
	} 
	
	/**
	 * Returns the value of a property.
	 * @param {String} property The property to return the corresponding value.
	 * @return The value of the property. The type depends on the property.
	 * @type {Any}
	 */
	this.getProperty = function(property){
  		switch (property) {
			case 'left':
				return this.getLeft();
				break;
			case 'top':
				return this.getTop();
				break;
			default:
				break;
		}
		var value = this.user_getProperty(property);
		return value;
	}
	
	/**
	 * Returns the properties of an EO in a JSON.
	 * @return A JSON with all the EO properties
	 * @type {Object}
	 */
	this.JSON = function(){
		var json =  this.user_JSON();
		json.left = this.getProperty('left');
		json.top = this.getProperty('top');
		if (this.propertyBox) {
			json.propertyBox = this.propertyBox.id;
		}
		if (this.instructionBox) {
			json.instructionBox = this.instructionBox.id;
		}
        return json;
	}
	
    // It's necessary to set the cancel property of the editionArea correctly to avoid problems with selection in IE.
    // If you don't do this, probably when you drag something you're going to select another thing at the same time.
	this.EOM.addToEditionAreaCancel('div[id=' + this.id +']');
		
	/**
	 * <b>Event</b></br>
	 * Defines what to do when the EO is appended.
	 */	
	this.onAppend = function(){
		if (this.propertyBox) {
			this.propertyBox.removeAllTabs();
		}	
		this.EOM.selectEO(this.id);
		if (this.user_onAppend) {
			this.user_onAppend();
		}
	} 
	
	/**
	 * Sets the property box where this EO is going to show its properties.
	 * @param {String} propertyBoxId The id of the property box to set to this EO.
	 */
	this.setPropertyBox = function(propertyBoxId){
		this.propertyBox = this.EOM.editionArea.workspace.getPropertyBoxById(propertyBoxId);
		this.onSetProperty(EO);
	}
	
	/**
	 * Sets the instruction box where the EO is going to show help to the user.
	 * @param {String} instructionBoxId The id of the instruction box to set to this EO.
	 */
	this.setInstructionBox = function(instructionBoxId){
		this.instructionBox = this.EOM.editionArea.workspace.getInstructionBoxById(instructionBoxId);
		this.onSetProperty(EO);
	}	
	
	/**
	 * Gets an HTML Object corresponding to some DOM node of the EO, based on the internal name of the object in this class. 
	 * @param {String} objectName The name of the object you need to be returned. Declared in the tool.js
	 * @return The HTML Object
	 * @type {DOM}
	 */
    this.getHTMLObject = function(objectName){
        // Returns an HTML object based on the internal name of the object in this class.
        return this[objectName];
    }
	
	/**
	 * Sets the left coordinate of the EO.
	 * @param {Int} left The left coordinate relative to the edition area to be set.
	 */
    this.setLeft = function(left){
        // Sets the left property of the div that contains the EO.
        var divEO = document.getElementById(this.id);
        var editionAreaWidth = this.EOM.editionArea.width;
        var divEORight = left + divEO.offsetWidth;
        var diff = editionAreaWidth - divEORight;
        
        if (left < 0) {
            divEO.style.left = '0px';
        }
        else {
            if (diff >= 0) {
                divEO.style.left = left + 'px';
            }
            else {
                divEO.style.left = left + diff + 'px';
            }
        }
		this.onSetProperty(EO);
    }

	/**
	 * Sets the top coordinate of the EO.
	 * @param {Int} top The top coordinate relative to the edition area to be set.
	 */    
    this.setTop = function(top){
        // Sets the left property of the div that contains the EO.
        var divEO = document.getElementById(this.id);
        var editionAreaHeight = this.EOM.editionArea.height;
        var divEOBottom = top + divEO.offsetHeight;
        var diff = editionAreaHeight - divEOBottom;
        
        if (top < 0) {
            divEO.style.top = '0px';
        }
        else {
            if (diff >= 0) {
                divEO.style.top = top + 'px';
            }
            else {
                divEO.style.top = top + diff + 'px';
            }
        }
		this.onSetProperty(EO);
    }
	
	/**
	 * Sets the width of the EO.
	 * @param {Int} width The width to be set.
	 */
    this.setWidth = function(width){
        var divEO = document.getElementById(this.id);
        
        divEO.style.width = width + 'px';
		this.onSetProperty(EO);
    }
    
	/**
	 * Returns the left coordinate of the EO.
	 * @return The left coordinate
	 * @type {Int}
	 */
    this.getLeft = function(){
        var divEO = document.getElementById(this.id);
        
        return divEO.offsetLeft;
    }
    
	/**
	 * Returns the top coordinate of the EO.
	 * @return The top coordinate.
	 * @type {Int}
	 */    
	 this.getTop = function(){
        var divEO = document.getElementById(this.id);
        
        return divEO.offsetTop;
    }
    
	/**
	 * Returns the width of the EO.
	 * @return The EO width
	 * @type {Int}
	 */
    this.getWidth = function(){
        var divEO = document.getElementById(this.id);
        
        return divEO.offsetWidth;
    }
    
	/**
	 * Returns the height of the EO.
	 * @return The EO height
	 * @type {Int}
	 */
    this.getHeight = function(){
        var divEO = document.getElementById(this.id);
        
        return divEO.offsetHeight;
    }
	
	/**
	 * Sets the snapTo of a particular EO. The movement is going to be restricted to moving in the x or y axis by multiples in pixels of this snapTo. 
	 * @param {Int} x The step in the x axis of the snapTo.
	 * @param {Int} y The step in the y axis of the snapTo.
	 */
	this.setSnapTo = function(x,y){
		$( "#" + this.id ).draggable('option', 'grid', [ x,y ]);
		var myX = this.getLeft();
		var myY = this.getTop();
		
		var restX = myX % x;
		var restY= myY % y;
		
		if (myX >= (x/2)){
			this.setLeft(myX + (x - restX));
		}
		
		if (myX < (x/2)){
			this.setLeft(myX - restX);
		}
		
		if (myY >= (y/2)){
			this.setTop(myY + (y - restY));
		}
		
		if (myY < (y/2)){
			this.setTop(myY - restY);
		}
		this.onSetProperty(EO);
	}
}
